package se.energimyndigheten.lampguiden.util;

import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import se.energimyndigheten.lampguiden.LightSource;
import se.energimyndigheten.lampguiden.bulbreplacement.ComparisonCalculator;
import se.energimyndigheten.lampguiden.lighting.Room;

public class AssetsUtil {

    public static final String JSON_PATH_LIGHTSOURCE = "submodule_html/lampdata.json";
    public static final String JSON_PATH_ROOMS = "submodule_html/Basta_ljuset/roomdata.json";

    public static final String HTML_PATH_CONVERSION_TABLE = "submodule_html/sv_Konverteringstabell.html";
    public static final String HTML_PLACEHOLDER = "$conversion_table$";
    public static final String HTML_PLACEHOLDER_R12 = "$conversion_table_r12$";
    public static final String HTML_PLACEHOLDER_R230 = "$conversion_table_r230$";

    public static final String JSON_PATH_CALCULATIONS = "submodule_html/savingscalculatordata.json";

    private static final String JSON_LIGHTSOURCES = "lamps";
    private static final String JSON_LIGHT_CAPTION = "caption";
    private static final String JSON_LIGHT_EFFECTS = "effects";
    private static final String JSON_LAMPTYPE = "type";
    private static final String JSON_LIGHT_ID = "image_id";
    private static final String JSON_CONVERSION_TABLE = "watt_lumen";

    private static final String JSON_CALC_HOURS = "calculation_hours";
    private static final String JSON_CALC_PRICE = "energy_price_kwh";
    private static final String JSON_CALC_TYPE = "types";
    private static final String JSON_CALC_TYPE_NAME = "name";
    private static final String JSON_CALC_TYPE_DOCUMENT = "document";
    private static final String JSON_CALC_TYPE_LIFE = "life_expectancy";
    private static final String JSON_CALC_TYPE_LUMEN = "max_lumen";
    private static final String JSON_CALC_TYPE_PRICES = "standard_prices";
    private static final String JSON_CALC_ENERGY_CLASS = "energy_class";
    private static final String JSON_CALC_REPLACE_WITH = "replace_with";
    private static final String JSON_CALC_ENERGY_RANK = "energy_rank";
    private static final String JSON_CALC_TYPE_EFFICIENCY = "power_need_relative_classic";

    private static final String JSON_ROOM_NAME = "name";
    private static final String JSON_ROOM_DOCUMENT = "document";
    private static final String JSON_ROOM_IMAGE = "image_name_format";
    private static final String JSON_ROOM_LIGHTSOURCE = "lamps";
    private static final String JSON_ROOM_HOTSPOT_X = "hotspot_x";
    private static final String JSON_ROOM_HOTSPOT_Y = "hotspot_y";


    /**
     * Get raw JSON data from file in assets.
     *
     * @param am   AssetManager
     * @param file path/filename in assets-folder
     * @return Raw JSON data
     * @throws java.io.IOException
     */
    public static String getFileContents(AssetManager am, String file) throws IOException {
        BufferedReader reader = new BufferedReader(new InputStreamReader(am.open(file)));

        Writer writer = new StringWriter();

        char[] buff = new char[2048];

        int n;
        while ((n = reader.read(buff)) != -1) {
            writer.write(buff, 0, n);
        }
        reader.close();
        return writer.toString();
    }

    /**
     * Get array of complete set of LightSources.
     *
     * @param json Raw json data
     * @return Array of LightSource objects.
     * @throws org.json.JSONException
     */
    public static LightSource[] getLightSourcesFromJson(String json) throws JSONException {

        JSONObject jObject = new JSONObject(json);
        JSONArray jsonLights = jObject.getJSONArray(JSON_LIGHTSOURCES);
        LightSource[] lights = new LightSource[jsonLights.length()];
        LightSource l;
        JSONObject j;
        for (int i = 0; i < jsonLights.length(); i++) {
            l = new LightSource();
            j = jsonLights.getJSONObject(i);

            l.setId(j.getInt(JSON_LIGHT_ID));
            l.setCaption(j.getString(JSON_LIGHT_CAPTION));

            if (j.has(JSON_LAMPTYPE)) {
                l.setLampType(/*LightSource.lampTypeFromString(*/j.getString(JSON_LAMPTYPE));
                JSONArray jEff = j.getJSONArray(JSON_LIGHT_EFFECTS);
                int[] eff = new int[jEff.length()];
                for (int k = 0; k < jEff.length(); k++) {
                    eff[k] = jEff.getInt(k);
                }
                l.setEffects(eff);
            }
            lights[i] = l;
        }
        return lights;
    }


    /**
     * Get a room object from matching room name.
     *
     * @param am The asset manager of the activity is needed to find json file
     * @param name Name of room you are looking for.
     * @return Room object or null
     * @throws org.json.JSONException
     * @throws java.io.IOException
     */
    public static Room getRoomFromName(AssetManager am, String name) throws JSONException, IOException {
        Room[] rooms = getRoomsFromJson(am);
        Room room = null;
        for (int i = 0, N = rooms.length; i < N; i++) {
            if (rooms[i].getName().equals(name)) {
                room = rooms[i];
                break;
            }
        }
        return room;
    }

    /**
     * Get array of room objects.
     *
     * @param am the asset manager of the activity is needed to find json file
     * @return Array of room objects
     * @throws org.json.JSONException
     * @throws java.io.IOException
     */
    public static Room[] getRoomsFromJson(AssetManager am) throws JSONException, IOException {
        //Log.v(TAG, "getRoomsFromJSON");

        JSONArray jsonRooms = new JSONArray(getFileContents(am, JSON_PATH_ROOMS));
        Room[] rooms = new Room[jsonRooms.length()];
        JSONObject jRoom;
        JSONObject jLight;
        JSONArray jLights;
        Room.Light[] lights;
        for (int i = 0, N = rooms.length; i < N; i++) {
            jRoom = jsonRooms.getJSONObject(i);
            rooms[i] = new Room(jRoom.getString(JSON_ROOM_NAME), jRoom.getString(JSON_ROOM_DOCUMENT), jRoom.getString(JSON_ROOM_IMAGE));
            jLights = jRoom.getJSONArray(JSON_ROOM_LIGHTSOURCE);
            lights = new Room.Light[jLights.length()];
            for (int k = 0, L = jLights.length(); k < L; k++) {
                jLight = jLights.getJSONObject(k);
                lights[k] = rooms[i].instantiateLight(jLight.getString(JSON_ROOM_DOCUMENT), jLight.getString(JSON_ROOM_HOTSPOT_X), jLight.getString(JSON_ROOM_HOTSPOT_Y));
            }
            rooms[i].setLights(lights);
        }
        return rooms;
    }

    /**
     * Get a drawable from assets
     * @param am Needed to read file from asset folder
     * @param name Name of the drawable, including file extension.
     * @return A drawable
     * @throws java.io.IOException
     */
    public static Drawable getDrawable(AssetManager am, String name) throws IOException {
        InputStream inputStream = am.open(name);
        Drawable drawable = Drawable.createFromStream(inputStream, name);
        return drawable;
    }

    /**
     * Scale an image with defined minimum dimensions. The full image is never loaded to save memory.
     *
     * @param res Resources needed
     * @param name path to image
     * @param width minimum width, typically width of container to hold the image
     * @param height minimum height, typically height of container to hold the image
     * @return Drawable image that is scaled of original.
     * @throws java.io.IOException
     */
    public static Drawable getScaledDrawable(Resources res, String name, int width, int height) throws IOException {
        InputStream inputStream = res.getAssets().open(name);

        // decode image size
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(inputStream, null, options);
        inputStream.close();
        inputStream = null;
        int imageWidth = options.outWidth;
        int imageHeight = options.outHeight;

        // decode full image pre-resized
        inputStream = res.getAssets().open(name);
        options = new BitmapFactory.Options();
        // re-size estimate
        options.inSampleSize = Math.max(imageWidth/width, imageHeight/height);
        // decode image
        Bitmap roughBitmap = BitmapFactory.decodeStream(inputStream, null, options);
        return (Drawable) new BitmapDrawable(res, roughBitmap);
    }

    /**
     * Get LightSource object at specific position
     *
     * @param json  Raw json data
     * @param index index of the light source to return
     * @return LightSource the light source at specified index
     * @throws org.json.JSONException
     */
    public static LightSource getLightSourceFromJson(String json, int index) throws JSONException {
        return getLightSourcesFromJson(json)[index];
    }

    public static Map<Integer, String> getConversionTableFromJson(String json, String lampType) throws JSONException {
        JSONObject jObject = new JSONObject(json);
        JSONObject jTable = jObject.getJSONObject(JSON_CONVERSION_TABLE).getJSONObject(lampType);
        return tableHelper(jTable);
    }

    private static Map<Integer, String> tableHelper(JSONObject jTable) throws JSONException {
        Map<Integer, String> map = new TreeMap<>();
        // iterate over objects and put into map
        JSONArray jTableKeys = jTable.names();
        for (int i = 0; i < jTableKeys.length(); i++) {

            int key = jTableKeys.getInt(i);
            String value = jTable.getString("" + key).replace("[", "").replace("]", "");
            map.put(key, value);
        }
        return map;
    }

    /**
     * Creates raw html from table with conversion data.
     *
     * @param data a map retrieved from {@link} getConversionTableFromJson
     * @return raw html to replace the place holder in tbody
     */
    public static String getParsedConversionTable(Map<Integer, String> data) {
        StringBuilder htmlTable = new StringBuilder();
        for (Entry<Integer, String> entry : data.entrySet()) {
            String row = "<tr><td><div>%1&nbsp;W</div></td><td><div>%2-%3&nbsp;lm</div></td></tr>";
            int key = entry.getKey();
            String[] value = entry.getValue().split(",");
            row = row.replace("%1", "" + key).replace("%2", value[0]).replace("%3", value[1]);
            htmlTable.append(row);
        }
        return htmlTable.toString();
    }

    public static ComparisonCalculator initComparsionCalculatorFromJson(String json) throws JSONException {
        JSONObject jObject = new JSONObject(json);

        double electricityPrice = jObject.getDouble(JSON_CALC_PRICE);
        int calculationHours = jObject.getInt(JSON_CALC_HOURS);

        JSONObject jTypes = jObject.getJSONObject(JSON_CALC_TYPE);

        LightSource[] lights = new LightSource[jTypes.length()];
        int i = 0;
        @SuppressWarnings("unchecked")
        Iterator<String> iter = jTypes.keys();
        while (iter.hasNext()) {
            String type = iter.next();
            JSONObject jO = jTypes.getJSONObject(type);
            String name = jO.getString(JSON_CALC_TYPE_NAME);
            String document = jO.getString(JSON_CALC_TYPE_DOCUMENT);
            int maxLumen = jO.getInt(JSON_CALC_TYPE_LUMEN);
            double efficiency = jO.getDouble(JSON_CALC_TYPE_EFFICIENCY);
            int lifeSpan = jO.getInt(JSON_CALC_TYPE_LIFE);
            JSONArray prices = jO.getJSONArray(JSON_CALC_TYPE_PRICES);
            JSONArray replace_with = jO.getJSONArray(JSON_CALC_REPLACE_WITH);
            int years = 10;
            int energyRank = jO.getInt(JSON_CALC_ENERGY_RANK);
            String energyClass = jO.getString(JSON_CALC_ENERGY_CLASS);
            lights[i++] = new LightSource(name, document, maxLumen, lifeSpan, prices, efficiency, type, years, replace_with, energyClass, energyRank);
        }

        return new ComparisonCalculator(calculationHours, electricityPrice, lights);
    }
}
